//
// Created by Yun on 2021/11/13.
//

#include <iostream>
#include <memory>
#include "GLWindow.h"

static void erro_callback(int error, const char* description)
{
    std::cerr << "Error: " << error << ",  " << description << std::endl;
}

GLWindowRef GLWindow::create(int width, int height, const std::string& title /*= ""*/)
{
    auto win = new GLWindow();
    win->init(width, height, title);
    return GLWindowRef{win};
}

void GLWindow::update()
{
    glfwPollEvents();
}

void GLWindow::setSize(int width, int height)
{
    this->size.x = (float)width;
    this->size.y = (float)height;
    glfwSetWindowSize(glfwWindow, (int)width, (int)height);
}

GLWindow::~GLWindow()
{
    // Free the windows callbacks and destroy the windows.
    glfwDestroyWindow(glfwWindow);

    // Terminate GLFW.
    glfwTerminate();
    glfwWindow = nullptr;
}

bool GLWindow::init(int width, int height, const std::string& title)
{
    glfwSetErrorCallback(erro_callback);

    if (!glfwInit())
    {
        std::cerr<< "Glfw init failed!" << std::endl;
        return false;
    }

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, resizable);

    this->size.x = (float)width;
    this->size.y = (float)height;
    glfwWindow = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
    if (nullptr == glfwWindow)
    {
        std::cerr << "Create GLFW windows failed" << std::endl;
        glfwTerminate();
        return false;
    }
    glfwSetWindowSizeLimits(glfwWindow, 200, 200, GLFW_DONT_CARE, GLFW_DONT_CARE);

    glfwSetWindowUserPointer(glfwWindow, this);
    glfwSetKeyCallback(glfwWindow, [](GLFWwindow* window, int key, int scancode, int action, int mods){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->keyEvent.setKey((KeyEvent::Key)key, (KeyEvent::KeyEventType)action);
        EventSystem::get()->dispatch(&win->keyEvent);
    });
    glfwSetCursorPosCallback(glfwWindow, [](GLFWwindow* window, double xPos, double yPos){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->mouseEvent.setMove((float)xPos, (float)yPos);
        EventSystem::get()->dispatch(&win->mouseEvent);
    });
    glfwSetMouseButtonCallback(glfwWindow, [](GLFWwindow* window, int button, int action, int mods){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->mouseEvent.setButton((MouseEvent::MouseButton)button, (MouseEvent::MouseEventType)action);
        EventSystem::get()->dispatch(&win->mouseEvent);
    });
    glfwSetScrollCallback(glfwWindow, [](GLFWwindow* window, double xOffset, double yOffset){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->mouseEvent.setScroll((float)xOffset, (float)yOffset);
        EventSystem::get()->dispatch(&win->mouseEvent);
    });

    glfwSetFramebufferSizeCallback(glfwWindow, [](GLFWwindow* window, int width, int height){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->size.x = (float)width;
        win->size.y = (float)height;
        win->windowEvent.eventType = WindowEvent::kWindowResize;
        win->windowEvent.width = win->size.x;
        win->windowEvent.height = win->size.y;
        EventSystem::get()->dispatch(&win->windowEvent);
    });

    glfwSetWindowFocusCallback(glfwWindow, [](GLFWwindow* window, int focused){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->windowEvent.eventType = WindowEvent::kWindowFocus;
        win->windowEvent.isFocus = focused == GLFW_TRUE;
        EventSystem::get()->dispatch(&win->windowEvent);
    });

    glfwSetWindowCloseCallback(glfwWindow, [](GLFWwindow* window){
        auto win = reinterpret_cast<GLWindow*>(glfwGetWindowUserPointer(window));
        win->windowEvent.eventType = WindowEvent::kWindowClose;
        EventSystem::get()->dispatch(&win->windowEvent);
    });

    return true;
}
